package warehouse;

import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Worker class creates the collections, runs through the Orderlist one by one,
 * Processes the orders and produces the report
 * 
 * @author Apeiranthitis, Grassos, Moschos
 * 
 */
public class Worker implements Runnable, Observer {

	OrderList allOrders;

	ItemList allItems;
	int workerNumber;
	String reportProcess = "\n\t PROCESSED ORDERS \n =====================================================================\n"
			+ "Order ID  Customer ID   Item ID   Quantity   Initial Price   Final Price \n";
	String reportFailed = "\n\n\t FAILED ORDERS \n =====================================================================\n";
	double finalPrice, totalGain, totalDiscount;
	int totalDeliveredOrders, totalFailedOrders;
	boolean check = true;

	/**
	 * Initializes the Worker Class constructor
	 * 
	 * @param allOrders
	 *            order list
	 * @param allItems
	 *            item list
	 */
	public Worker(OrderList allOrders, ItemList allItems, int workerNumber) {

		this.allOrders = allOrders;
		this.allItems = allItems;
		this.workerNumber = workerNumber;

	}

	/**
	 * Manages the orders, firstly checking if it is not in the end of our
	 * Collection and if so, calls the processOneOrder() method. Then, it prints
	 * the appropriate statistics calling the printStatistics() and
	 * writeReport(String file, String text) methods
	 */
	public void run() {

		while (allOrders.hasOrder()) {
			try {
				Thread.sleep(1000);
				if (check == true) {

					processOneOrder();

					check = false;
				}
				check = true;
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				System.out.println(e.getMessage());
			}

		}
		printStatistics();
		printReport(workerNumber);

	}

	/**
	 * Processes each order, getting all details from the next order and calling
	 * calculateDiscountPrice() method, writing whatever is required in report
	 * text. Additionally, it catches a potential NullPointerException whenever
	 * an Item Id is referred and does not exist.
	 * 
	 */
	public synchronized void processOneOrder() {

		Order order = allOrders.getNextOrder();
		// find the item id
		String itemId = order.getItemID();
		// find the item in warehouse
		Item m = allItems.getItembyId(itemId);
		// check for item availability using the quantity
		int oQuantity = order.getOrderQuantity();
		try {
			int wQuantity = m.getQuantity();
			if (wQuantity >= oQuantity) {
				wQuantity = wQuantity - oQuantity;
				m.setQuantity(wQuantity);

				reportProcess += String.format("%-10s", order.getOrderID())
						+ String.format("%-14s", order.getCustomerID())
						+ String.format("%-10s", order.getItemID())
						+ String.format("%-11s", +order.getOrderQuantity());
				Gui.setDisplay(printOrder(order), workerNumber);
				update();
				double basicPrice = order.getOrderQuantity() * m.getPrice();
				double finalPrice = calculateDiscountPrice(basicPrice,
						order.getCustomerID(), order.getOrderQuantity());
				reportProcess += String.format("%-16s",
						String.format("%.2f", basicPrice) + "   GBP")
						+ String.format("%-16s",
								String.format("%.2f", finalPrice) + "   GBP ")
						+ "\n";
				totalGain += finalPrice;
				totalDiscount += basicPrice - finalPrice;
				totalDeliveredOrders++;

			} else {
				Gui.setDisplay(printOrder(order), workerNumber);
				update();
				reportFailed += "Order "
						+ order.getOrderID()
						+ " for "
						+ order.getOrderQuantity()
						+ " items (Item ID "
						+ order.getItemID()
						+ ") could not be processed due to item shortage in warehouse\n";
				totalFailedOrders++;

			}
		} catch (NullPointerException npe) {
			System.out.println("item id " + order.getItemID()
					+ " cannot be found in warehouse");
		}

	}

	public String printOrder(Order order) {
		String reportOrder = String.format("%-4s", order.getOrderID())
				+ String.format("%-6s", order.getCustomerID())
				+ String.format("%-4s", order.getItemID())
				+ String.format("%-4s", +order.getOrderQuantity());
		return reportOrder;
	}

	/**
	 * Writes the report to file using a FileWriter object and catching either a
	 * potential FileNotFoundException or an IOException
	 * 
	 * @param filename
	 * @param report
	 */
	public void writeReport(String filename, String report) {

		FileWriter writer;
		try {

			writer = new FileWriter(filename);
			writer.write(report);
			writer.close();
		}
		// if the file has not been found catch the exception and display the
		// suitable message
		catch (FileNotFoundException fnf) {
			System.out.println(filename + " not found ");
			System.exit(0);
		}
		// this catch clause, manages the possibility of Stack overflow during
		// IO process
		catch (IOException ioe) {
			ioe.printStackTrace();
			System.exit(1);
		}
	}

	/**
	 * This method is responsible for the Discount calculations, depending on
	 * customer`s level, reading the first character of its ID and then looking
	 * up and comparing the quantity
	 * 
	 * @param bPrice
	 *            basic price (quantity*price)
	 * @param cId
	 *            the customer id
	 * @param quantity
	 *            order quantity
	 * @return the final price after discount calculation
	 */
	public synchronized static double calculateDiscountPrice(double bPrice,
			String cId, int quantity) {
		double price;
		if (cId.charAt(0) == 'A') {
			price = bPrice - bPrice * 15 / 100;
		} else if (cId.charAt(0) == 'B') {
			price = bPrice - bPrice * 5 / 100;
		} else
			price = bPrice;
		if (quantity >= 100) {
			price = price - price * 10 / 100;
		} else if (quantity < 100 && quantity >= 60) {
			price = price - price * 5 / 100;
		}
		return price;
	}

	/**
	 * Prints statistics about orders, catching an potential ArithmeticException
	 * in case no order at all is processed
	 */
	private void printStatistics() {
		try {
			double successPercentage = 100 * totalDeliveredOrders
					/ (totalDeliveredOrders + totalFailedOrders);
			reportProcess += "\n Total Gain for our warehouse : "
					+ String.format("%.2f", totalGain) + " GBP";
			reportProcess += "\n Total Discount amount for delivered orders : "
					+ String.format("%.2f", totalDiscount) + " GBP";
			reportProcess += "\n Delivered Orders : " + totalDeliveredOrders;
			reportProcess += "\n Unsuccessful Orders : " + totalFailedOrders
					+ "\n";
			reportProcess += "\n Successful Orders percentage: "
					+ successPercentage + "%\n";
		} catch (ArithmeticException ae) {
			System.out.println("No orders to process");
		}
	}

	public void printReport(int workerNumber) {
		String filename = "Report" + workerNumber + ".txt";

		writeReport(filename, "WORKER " + workerNumber + "\n" + reportProcess
				+ reportFailed);
	}

	@Override
	public void update() {

		Gui.displayOrders.setText(allOrders.listDetails());
		Gui.displayItems.setText(allItems.printItems());
		
	}

}